General Remarks
- Group work is NOT allowed in the lab. You have
to work alone. Discussions with colleagues (e.g., in the forum) are
allowed but the code has to be written alone.
- The assignment contains three parts: a chat client/server
implementation, an implementation of a direct connection between
clients, and a test client. We suggest you implement one after another.
- Be sure to check the Tricky Parts section for questions!
- Further reading is provided under tutorials
Submission Guide (for all Labs)
- You must upload your solution using the Teaching Tool before the submission deadline: 05.11.2007, 13:00.
- Before the submission deadline, you can upload your solution as often as you like.
- After the submission deadline, there will be a mandatory
interview (Abgabegespräch). You must register for a time slot to the
interviews using the Teaching Tool.
- The interview will take place in our lab. During the
interview, you will be asked about the solution that you have uploaded
(changes after the deadline will not be taken into account!). In the
interview you need to explain your code, design and architecture in
detail.
- You may pass the interviews beginning two weeks before
the main "interview" - week. You need to submit your solution to the
teaching tool and explain your assignment during a tutor interview. Benefit: Faster passing of lab 1. Drawback: Less time to code ;)
- Remember that you can do the interview only once!
- Your submission must compile and run in our lab environment. Please test before you submit. Please provide ant-tasks to ease testing. You may use our ant template.
- Please upload as a ZIP file. Please submit only the sources
of your solution (not the compiled class files). Do not use or include
third-party libraries.
Description
In this assignment you will learn:
- the basics of socket communication
- how to program multithreaded
- different connection types
In this year's DSLAB you will develop a fancy socket based chat system. You need to implement two components:
| Client
| The client connects to the server to announce
chat availability. It accepts simple console Input and sends the
message to the server. Beyond simple messaging functionality, clients
are able to search for currently connected clients and establish chats
exclusively (on separate connections) with other clients. We call this
feature Direct Chat.
|
| Server
| The server accepts client connections and
executes client queries. To simplify matters the server runs in an
infinite loop. To end the server you simply kill the executing jobs
(for Details please refer to Server)
|
Sockets
Most interprocess communication uses the client server model. These terms
refer to the two processes which will be communicating with each other. One of
the two processes, the client, connects to the other process, the server,
typically to make a request for information. A good analogy is a person who
makes a phone call to another person.
The system calls for establishing a connection are somewhat different for the
client and the server, but both involve the basic construct of a socket. A
socket is one end of an interprocess communication channel. The two processes
each establish their own socket.
Client
The basic steps involved in establishing and using a socket on the client side are as follows:
- Create a socket with the Socket Java class and connect the
socket to the address of the server. In the DSLAB, the server runs on
the local machine. That means you need to create a Socket for localhost.
- Send and receive data.
There are a number of ways to do this, but the simplest is to use the readLine() and write() methods of the BufferedReader and BufferedWriter classes.
Listing 1 provides sample code to do this:
 |
| Listing 1 |
The client should read lines from the Input Console and send each line to the server. To terminate the client the string
!end
must be entered.
 |
| Listing 2 |
Listing 2 provides a sample chat sequence. After each line, the client sends data to the server. If the input equals !end the client aborts communication and terminates. Be sure to release all resources and close all sockets!
Be sure to send the message according to the following format:
USERNAME [dd.MM.yyyy HH:mm:ss]: MESSAGE
Until now, we implemented a simple input system and send each line to
the server. But how does a client receive our messages? For this
purpose we use a Thread (let's refer to this as the ReceiverThread). The thread allocates a socket on the same host (localhost) and on the same port. It retrieves an InputStream from the Socket and a BufferedReader from this InputStream. Simply write the data read from the BufferedReader to the console.
To realize the Direct Chat
functionality, we create another Thread in the Client! This thread is
spawned, if we receive a direct chat request from another client. To
ensure compatibility with our lab port policy
we create a new listening Socket on our base port + 1 (f.e. user
dslab900: 10000 + 900 * 10 => Base Port = 19000; Base Port + 1 =
19001).
This Socket accepts connections and deals with so called
direct chat requests from other clients. A direct chat request
establishes a direct connection from one client to another without the
control of the server.
| Note | Please note that this is a rather unsecure communication setting (as we are using no message encryption)! |
After the listening Socket accepts a connection, a new Thread (let's refer to this as the DirectChatThread) is spawned. Every message received from the direct chat partner should be printed to the console (as the ReceiverThread does). A sample chat sequence is illustrated in Listing 3. The ReceiverThread
should not print messages from the server during a Direct Chat! It is
not possible to chat with other users in the direct chat mode. Only
direct chat with one user (in our example: Bob) is possible! Other
direct chat requests must must be rejected by sending a simple reject
message.
 |
| Listing 3 |
The user chats with Alice by the use of the server
connection. Every other client connected to the server receives these
messages. Bob requests a direct chat with the user. The Client must
implement this request by asking the user to accept this request. If
the user accepts, a new chat sequence is initiated. To end this direct
chat session, the Client awaits the input of !end. If the input equals this string, the DirectChatThread stops and the client changes into server - chat mode.
| Ports |
This pretty basic chat scenario has a big drawback: The number of
simultaneous executing clients is limited to the available port
numbers. We know that, of course ;) Due to simplicity, we reduce this
scenario to a maximum of four participants: 1 Server, 3 clients. During
the interview with a tutor in the DSLAB, you need to present a scenario
with the following setting: 1 Server running, 3 clients connected to
the server. Two of them establish a direct chat and after a small chat
resume to server chat mode. |
Scenario 1 illustrates the server client chat mode.
 |
| Scenario 1 |
Server
A threaded socket-based server is probably the easiest server type to
understand. A server program almost always needs to handle more than
one connection at a time. The reason is, since each connection gets
its own thread, each thread can use simple blocking I/O on the
socket. All other multi-connection server types use non-synchronous
I/O of varying complexities in order to avoid thread overhead.
| Lab port policy | If
your server tries to bind to a TCP port that is already listening for a
connection, an exception will be thrown by your server. Because many
people will be testing their servers in the lab environment, such
binding errors are probable. In order to prevent this from happening,
we suggest that you use your dslabXXX number when choosing a port. For
example, if your dslab number is dslab900, you should use 10000 + 900 * 10 = 19000 as the server port. |
To implement this functionality you need to start a
Server Thread. This Thread implements an infinite loop (please refer to
server stop problems in the tricky parts
section for stop problems). When a client connects to the Server
Socket, a new Client Thread is spawned and handles all client requests.
For each connecting client the server spawns a new ClientThread. By this architecture, a clean resource handling is guaranteed. After the ClientThread was successfully started the client list needs to be updated. This list stores each client, it's address and it's port:
Userlist example for DSLAB User 456:
| | Username | Host | Port |
| 1.Entry | Client01 | localhost | 14560 |
| 2.Entry | Client02 | localhost | 14562 |
| 3.Entry | Client03 | localhost | 14564 |
Every time the ClientThread
receives a message, it distributes it to all currently connected
clients. You need to ensure this message distribution! But before the
server broadcasts every message, some simple "protocol analysis" has to
be realized.
If the message matches to
!getConnectedClients
the
server needs to send the list of all currently connected clients to the
requesting client. Based on the information provided in this server
response, clients are able to establish a direct chat connection to
other participants. Be sure to send this information only to the
requesting client! It is not necessary to distribute this information
to all participating clients. The client receives the requested
information and selects a partner for the direct chat. After that he
contacts the selected direct chat partner directly. The requested chat
partner receives an invitation and is asked for acceptance for a direct
chat with the requester. In case of acceptance, the chat is
established. Otherwise the requester receives an error message. A
successful direct chat connection is traced in listing 3. The described
scenario is illustrated in scenario 2.
 |
| Scenario 2 |
Listing 4 illustrated the direct chat partner search.
[Time information is skipped for simplicity].
 |
| Listing 4 |
Connection Setup
The basic connection technique in this scenario is using ServerSockets and Sockets.
Client [Main application]:
- Reading input from a streamreader
- Formating Date
- Send the formated message to the server via a socket
ReceiverThread:
- Reading input from streamreader from the server
- Printing messages from the server to the console
DirectChatThread:
- Creating a ServerSocket on ports according to our port policy
- Ask the user for acceptance of the incoming direct chat request
- Spawn a new DirectChatThread for each incoming connection (in case of acceptance)
The Client Input and the DirectChatThread have to check the user input for the following two commands:
!end
This
command terminates communication. If the client is currently in direct
chat - mode, it resumes server chat - mode. If the client is in server
chat - mode it simply terminates.
!getConnectedClients
Send
a message to the server, querying for the list of all currently
connected clients. The layout of the data structure can be retrieved
from user list.
Test Client
Every software needs to be tested. Consequently, our DSLAB Lab 1
should be tested too. You need to implement a Test Client, using the JUnit Framework. The JUnit libraries can be found in /usr/share/ant/lib on the lab server. A pretty good introduction into the new features of JUnit 4.0 are described in JUnit 4.0 in 10 minutes. The Test Client should test the following unit tests:
1. Connect to the server
This test case should connect to the server and test simple connectivity. The test fails, when the server is not available.
2. Send sample message
In
this test case, the test client connects to the server and sends some
test messages. It can be tested if some messages come back, but this is
not a must!
3. Request Client list
Send !getConnectedClients to the server and test the correctness of the returned user list.
With the use of our provided ANT - file
 |
| Testing with junit |
Tricky parts
After hours of development you compile, start your engine and...
fail! No worries, here is some advice:
Compilation problems
You cannot compile your solution. Here is some help: Use our ANT file and start by typing:
ant
This command searches for the build.xml - file and executes it.
Binding problems
After starting your socket implementation, you receive a message comparable to Error 1:
 |
| Error 1 |
The address and port, you are trying to bind to is
already in use. To solve these issues you can test your solution at
home (and don't bind to ports used by other services ;), or you simply
change the ports. Our policy to use your dslab account number prevents
these errors! For example: DSLAB023 = Bind to port 10230
Server stop problems
Your developed your server and implemented and infinite loop. You
started it, but you cannot stop it? So, get your keyboard and do the
following:
 |
| Error 2 |
The first line starts the server.
java {packagenames}.[serverclass] & starts the server. The ampersand (&) is important to spawn a new job in unix.
jobs lists all currently active jobs.
ps ax | grep [serverclass] retrieves the jobnumber (in the previous example 11180)
kill [jobnumber] kills the server.
Now you stopped the server and you are able to fix errors in your source code, compile and start again.
Proposed Tutorials
Java Custom Networking Tutorial
[http://java.sun.com/docs/books/tutorial/networking/index.html]
Java Sockets Tutorial
[http://java.sun.com/docs/books/tutorial/networking/sockets/index.html]
Java Concurrency Tutorial
[http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html]